package com.bicntech.system.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bicntech.common.utils.ObjectUtil;
import com.bicntech.common.utils.StringUtils;
import com.bicntech.system.dto.InfieldBasicDataDTO;
import com.bicntech.system.dto.PerformanceQueryDTO;
import com.bicntech.system.entity.PersonnelPerformanceManagement;
import com.bicntech.system.entity.UserInfo;
import com.bicntech.system.mapper.PersonnelPerformanceManagementMapper;
import com.bicntech.system.service.InfieldBasicDataService;
import com.bicntech.system.service.PerformanceScoreConfigService;
import com.bicntech.system.service.PersonnelPerformanceManagementService;
import com.bicntech.system.service.UserInfoService;
import com.bicntech.system.vo.PersonnelPerformanceManagementVO;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 *
 * @author bicntech
 * @description:人员绩效管理 服务实现类
 * </p>
 * @date 2023-08-07
 */
@Slf4j
@Service
public class PersonnelPerformanceManagementServiceImpl extends ServiceImpl<PersonnelPerformanceManagementMapper, PersonnelPerformanceManagement> implements PersonnelPerformanceManagementService {

    @Resource
    private UserInfoService userInfoService;

    @Resource
    private PerformanceScoreConfigService performanceScoreConfigService;

    @Resource
    private PersonnelPerformanceManagementMapper performanceManagementMapper;

    @Resource
    private InfieldBasicDataService infieldBasicDataService;

    @Override
    public IPage<PersonnelPerformanceManagementVO> listByPage(IPage<PersonnelPerformanceManagement> packPage, PerformanceQueryDTO queryDTO) {
        List<String> userIdList = userInfoService.selectByUserName(queryDTO.getUserName(), queryDTO.getDepartmentId());
        IPage<PersonnelPerformanceManagement> performanceManagementPage = lambdaQuery()
                .like(StrUtil.isNotBlank(queryDTO.getYears()), PersonnelPerformanceManagement::getYears, queryDTO.getYears())
                .eq(ObjectUtil.notEmpty(queryDTO.getSceneType()), PersonnelPerformanceManagement::getSceneType, queryDTO.getSceneType())
                .in(CollUtil.isNotEmpty(userIdList), PersonnelPerformanceManagement::getUserId, userIdList)
            //    .isNull(CollUtil.isEmpty(userIdList), PersonnelPerformanceManagement::getId)
                .orderByDesc(PersonnelPerformanceManagement::getYears)
                .page(packPage);
        ImmutableMap<String, UserInfo> mapByUserId = this.getUserInfoList(performanceManagementPage.getRecords());
        List<InfieldBasicDataDTO> basicDataDTOList = infieldBasicDataService.listInfieldBasicData();
        return performanceManagementPage.convert(a -> {
            PersonnelPerformanceManagementVO managementVO = ObjectUtil.entityToModel(a, PersonnelPerformanceManagementVO.class);
            Optional.ofNullable(mapByUserId.get(managementVO.getUserId()))
                    .ifPresent(it -> {
                        managementVO.setSceneType(String.valueOf(a.getSceneType()));
                        managementVO.setUserName(it.getUserName());
                        managementVO.setDepartmentName(it.getDepartmentName());
                    });
            managementVO.setInspectionItem(this.translationKey(managementVO.getInspectionItem(), a.getSceneType(), basicDataDTOList));
            return managementVO;
        });
    }

    @Override
    public void addMark(String userId, Integer sceneType, Long inspectionItem, Date inspectDate) {
        if (StrUtil.isBlank(userId)) return;
        BigDecimal score = performanceScoreConfigService.getScore(sceneType, inspectionItem);
        String years = this.getYears(inspectDate);
        synchronized (userId.intern()) {
            PersonnelPerformanceManagement management = super.lambdaQuery()
                    .eq(PersonnelPerformanceManagement::getYears, years)
                    .eq(PersonnelPerformanceManagement::getSceneType, sceneType)
                    .eq(PersonnelPerformanceManagement::getUserId, userId)
                    .one();
            if (ObjectUtil.notEmpty(management)) {
                management.setMark(score.add(management.getMark()));
                Map<String, Integer> inspectionItemMap = management.getInspectionItem();
                this.addFrequency(inspectionItemMap, String.valueOf(inspectionItem));
                management.setInspectionItem(inspectionItemMap);
            } else {
                Map<String, Integer> integerMap = this.initializeInspectionItem(sceneType);
                this.addFrequency(integerMap, String.valueOf(inspectionItem));
                management = new PersonnelPerformanceManagement()
                        .setMark(score)
                        .setSceneType(sceneType)
                        .setInspectionItem(integerMap)
                        .setUserId(userId)
                        .setYears(years);

            }
            super.saveOrUpdate(management);
        }
    }

    @Override
    public void recallMark(String userId, Integer sceneType, Long inspectionItem, Date inspectDate) {
        if (StrUtil.isBlank(userId)) return;
        BigDecimal score = performanceScoreConfigService.getScore(sceneType, inspectionItem);
        String years = this.getYears(inspectDate);
        synchronized (userId.intern()) {
            PersonnelPerformanceManagement management = super.lambdaQuery()
                    .eq(PersonnelPerformanceManagement::getSceneType, sceneType)
                    .eq(PersonnelPerformanceManagement::getYears, years)
                    .eq(PersonnelPerformanceManagement::getUserId, userId)
                    .one();
            management.setMark(management.getMark().subtract(score));
            Map<String, Integer> inspectionItemMap = management.getInspectionItem();
            this.subtractFrequency(inspectionItemMap, String.valueOf(inspectionItem));
            management.setInspectionItem(inspectionItemMap);
            super.updateById(management);
        }
    }

    @Override
    public List<PersonnelPerformanceManagementVO> selectListByYears(String years, List<InfieldBasicDataDTO> basicDataDTOList) {
        List<PersonnelPerformanceManagementVO> performanceManagementVOList = performanceManagementMapper.selectUserPerformanceByYears(years);
        performanceManagementVOList
                .stream()
                .filter(f -> StrUtil.isNotBlank(f.getSceneType()))
                .forEach(a -> a.setInspectionItem(this.translationKey(a.getInspectionItem(), Integer.valueOf(a.getSceneType()), basicDataDTOList)));
        return performanceManagementVOList;

    }


    private String getYears(Date inspectDate) {
        return DateUtil.format(inspectDate, "yyyy-MM");
    }

    private ImmutableMap<String, UserInfo> getUserInfoList(List<PersonnelPerformanceManagement> list) {
        if (CollUtil.isEmpty(list)) return ImmutableMap.of();
        List<String> wxUserId = list
                .stream()
                .map(PersonnelPerformanceManagement::getUserId)
                .collect(Collectors.toList());
        return Maps.uniqueIndex(userInfoService.getByIds(wxUserId), UserInfo::getUserId);
    }

    private void addFrequency(Map<String, Integer> inspectionItemMap, String item) {
        inspectionItemMap = Optional.ofNullable(inspectionItemMap).orElse(new HashMap<>());
        Integer frequency = ObjectUtil.isEmptyObject(inspectionItemMap.get(item)) ? 1 : (inspectionItemMap.get(item) + 1);
        inspectionItemMap.put(item, frequency);
    }

    private void subtractFrequency(Map<String, Integer> inspectionItemMap, String item) {
        inspectionItemMap = Optional.ofNullable(inspectionItemMap).orElse(new HashMap<>());
        Integer frequency = ObjectUtil.isEmptyObject(inspectionItemMap.get(item)) ? -1 : (inspectionItemMap.get(item) - 1);
        inspectionItemMap.put(item, frequency);
    }

    private Map<String, Integer> initializeInspectionItem(Integer sceneType) {
        Map<String, Integer> map = new HashMap<>();
        if (ObjectUtil.equals(1, sceneType)) {
            infieldBasicDataService.listInfieldBasicData()
                    .forEach(it -> map.put(String.valueOf(it.getId()), 0));
        } else {
            for (int i = 1; i <= 4; i++) {
                map.put(String.valueOf(i), 0);
            }
        }
        return map;
    }

    private Map<String, Integer> translationKey(Map<String, Integer> map, Integer sceneType, List<InfieldBasicDataDTO> basicDataDTOList) {
        if (MapUtil.isEmpty(map)) return map;
        Map<String, Integer> returnMap = new HashMap<>(map.size());
        map.forEach((k, v) -> {
            if (ObjectUtil.equals(1, sceneType)) {
                returnMap.put(this.getInfieldVehicleType(k, basicDataDTOList), v);
            } else {
                returnMap.put(this.getOutfieldVehicleType(k), v);
            }
        });
        return returnMap;
    }


    private String getOutfieldVehicleType(String type) {
        switch (type) {
            case "1":
                return "接机工作";
            case "2":
                return "送机工作";
            case "3":
                return "维修检查";
            case "4":
                return "长短停及航后离机检查项目";
            default:
                return StringUtils.EMPTY;
        }
    }

    private String getInfieldVehicleType(String type, List<InfieldBasicDataDTO> basicDataDTOList) {
        return basicDataDTOList.stream().filter(it -> ObjectUtil.equals(it.getType(), Integer.valueOf(type)))
                .findFirst()
                .map(InfieldBasicDataDTO::getApplicableModels)
                .orElse(StringUtils.EMPTY);
    }
}
